#include <mbed.h>

#include "baidu_recorder.h"
#include "baidu_media_adapter.h"
#include "baidu_speex_encoder.h"

#if defined(TARGET_UNO_91H)
#include "gpadckey.h"
#include "SDMMCFileSystem.h"
#elif defined(TARGET_K64F)
#include "SDFileSystem.h"
#else
#error "Not supported"
#endif

#define __STORE_WAV__ // save voice record in wav format to sd card
#define SAMPLE_RATE_16K // sample rate is 16k, undefine will use 8k
// press button begin record, then press again stop record
// undefine means press down begin record, release will stop record
#define PRESS_START_PRESS_STOP

//begin  just for compile
void* baidu_get_netstack_instance() {
    return NULL;
}
#ifdef __cplusplus
extern "C" {
#endif
unsigned int OTA_FW_INFO_ADDR = 0x18003000;  //COPY_MODE_FW_INFO_ADDR
unsigned int OTA_UPGRADE_ADDR = 0x18104000;  //COPY_MODE_UPGRADE_ADDR
unsigned int OTA_UPGRADE_AREA_SIZE = 0x100000; //COPY_MODE_FW_MAX_SIZE, must be 4K aligned
#ifdef __cplusplus
}
#endif
//end  just for compile

#define RLOG(_fmt, ...)  mbed_error_printf("[%s]"_fmt"\n", __func__, ##__VA_ARGS__)

#define SDCARD_PATH     "sdcard"

typedef enum _button_status_enum {
    BTN_RELEASE,
    BTN_PRESS,
    BTN_LONG_PRESS
} btn_status_t;

typedef struct _pcm_header_t {
    char    _pcm_header[4];
    size_t  _pcm_length;
    char    _format[8];
    int     _bit_rate;
    short   _pcm;
    short   _channel;
    int     _sample_rate;
    int     _byte_rate;
    short   _block_align;
    short   _bits_per_sample;
    char    _fix_data[4];
    size_t  _data_length;
} pcm_header_t;

static rtos::Queue<void, 10> _queue;

static pcm_header_t g_pcm_header = {
    {'R', 'I', 'F', 'F'},
    (size_t)-1,
    {'W', 'A', 'V', 'E', 'f', 'm', 't', ' '},
    0x10,
    0x01,
    0x01,
#ifdef SAMPLE_RATE_16K
    0x3E80,
    0x7D00,
#else //8k
    0x1F40,
    0x3E80,
#endif // SAMPLE_RATE_16K
    0x02,
    0x10,
    {'d', 'a', 't', 'a'},
    (size_t)-1
};

class TestStoreWav: public duer::Recorder::IListener {
public:

    TestStoreWav()
        : _file(NULL)
        , _count(0)
    {
    }

    virtual int on_start() {
        RLOG("start");
        if (_file) {
            fclose(_file);
            _file = NULL;
        }

        char _name[64];
        sprintf(_name, "/"SDCARD_PATH"/%08d.wav", _count++);
        _file = fopen(_name, "wb");
        if (!_file) {
            RLOG("can't open file %s", _name);
            return -1;
        } else {
            RLOG("begin write to file:%s", _name);
        }
        fwrite(&g_pcm_header, 1, sizeof(g_pcm_header), _file);
        g_pcm_header._data_length = 0;
        g_pcm_header._pcm_length = sizeof(g_pcm_header) - 8;
        return 0;
    }

    virtual int on_resume() {
        RLOG("done");
        return 0;
    }

    virtual int on_data(const void *data, size_t size) {
        RLOG("data");
        if (_file) {
            fwrite(data, 1, size, _file);
            g_pcm_header._data_length += size;
        }
        return 0;
    }

    virtual int on_pause() {
        RLOG("done");
        return 0;
    }

    virtual int on_stop() {
        RLOG("stop");
        if (_file) {
            g_pcm_header._pcm_length += g_pcm_header._data_length;
            fseek(_file, 0, SEEK_SET);
            fwrite(&g_pcm_header, 1, sizeof(g_pcm_header), _file);
            fclose(_file);
            _file = NULL;
            RLOG("finish the record");
        }
        return 0;
    }

private:

    FILE *_file;
    int _count;
};

class TestStoreEncoder: public duer::Recorder::IListener {
public:

    TestStoreEncoder()
        : _file(NULL)
        , _count(0)
    {
    }

    virtual int on_start() {
        RLOG("done");
        if (_file) {
            fclose(_file);
            _file = NULL;
        }

        char _name[64];
        sprintf(_name, "/"SDCARD_PATH"/%08d.spx", _count++);
        _file = fopen(_name, "wb");
        if (!_file) {
            RLOG("can't open file %s", _name);
        }
        return 0;
    }

    virtual int on_resume() {
        RLOG("done");
        return 0;
    }

    virtual int on_data(const void *data, size_t size) {
        RLOG("done");
        if (_file) {
            fwrite(data, 1, size, _file);
        }
        return 0;
    }

    virtual int on_pause() {
        RLOG("done");
        return 0;
    }

    virtual int on_stop() {
        RLOG("done");
        if (_file) {
            fclose(_file);
            _file = NULL;
        }
        return 0;
    }

private:

    FILE *_file;
    int _count;
};

static void button_press() {
    RLOG("trigger");
    _queue.put((void *)BTN_PRESS);
}

static void button_release() {
    RLOG("trigger");
    _queue.put((void *)BTN_RELEASE);
}

int main()
{
    RLOG("!!!Recorder Test!!!");

#if defined(TARGET_UNO_91H)
    mbed::GpadcKey  _button = mbed::GpadcKey(KEY_A0);
    SDMMCFileSystem _sdcard = SDMMCFileSystem(PB_9, PB_0, PB_3, PB_7, PC_0, PC_1, SDCARD_PATH);
#elif defined(TARGET_K64F)
    mbed::InterruptIn _button = mbed::InterruptIn(SW3);
    SDFileSystem _sdcard = SDFileSystem(D11, D12, D13, D10, SDCARD_PATH);
#endif

    osEvent evt;

    _button.fall(&button_press);
#ifndef PRESS_START_PRESS_STOP
    _button.rise(&button_release);
#endif

    duer::MediaAdapter adapter;
    duer::Recorder _recorder(&adapter);

#if defined(__STORE_WAV__)
    TestStoreWav _store_wav;
    _recorder.set_listener(&_store_wav);
#else
    duer::SpeexEncoder _encoder;
    TestStoreEncoder _store_spx;
    _encoder.set_listener(&_store_spx);
    _recorder.set_listener(&_encoder);
#endif
#ifdef PRESS_START_PRESS_STOP
    bool record_started = false;
#endif
    RLOG("Ready to record...");
    while (true) {
        evt = _queue.get();
        if (evt.status != osEventMessage) {
            RLOG("Error status...");
            continue;
        }

        switch (evt.value.v) {
        case BTN_PRESS:
#ifdef PRESS_START_PRESS_STOP
            if (!record_started) {
                record_started = true;
                _recorder.start();
            } else {
                _recorder.stop();
                record_started = false;
            }
#else
            _recorder.start();
#endif // PRESS_START_PRESS_STOP
            break;
        case BTN_RELEASE:
            _recorder.stop();
            break;
        default:
            RLOG("Error events...");
            break;
        }
    }

    return 0;
}
